﻿using System.ComponentModel.DataAnnotations;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;

using zijian666.Core;
using zijian666.DI.Abstractions;
using zijian666.DI.Defaults;

namespace zijian666.DI;

/// <summary>
/// 自动装配扩展方法
/// </summary>
public static class AutowiredExtensions
{
    /// <summary>
    /// 线程上下文
    /// </summary>
    [ThreadStatic]
    private static IAutowiredContext? _currentContext = null;

    /// <summary>
    /// 有缺陷，请使用 <see cref="Autowired{T}(IServiceProvider, Func{T})"/>"/>
    /// </summary>
    /// 
    [Obsolete("有缺陷")]
    public static T Autowired<T>(this IServiceProvider services, T instance)
    {
        if (instance is null)
        {
            return instance;
        }

        if (_currentContext is not null)
        {
            _currentContext.Add(instance);
            return instance;
        }

        try
        {
            using var context = services.GetService<IAutowiredContext>() ?? new DefaultAutowiredContext(services);
            _currentContext = context;
            _currentContext.Add(instance);
            context.Wired();
            return instance;
        }
        finally
        {
            _currentContext = null;
        }
    }

    /// <summary>
    /// 自动装配
    /// </summary>
    /// <param name="services">服务提供程序</param>
    /// <param name="instance">待装配的实例对象</param>
    /// <returns></returns>
    public static T Autowired<T>(this IServiceProvider services, Func<T> fn)
    {
        if (fn is null)
        {
            return default!;
        }
        if (_currentContext is not null)
        {
            var instance = fn();
            if (instance is not null)
            {
                _currentContext.Add(instance);
            }
            return instance;
        }

        try
        {
            using var context = services.GetService<IAutowiredContext>() ?? new DefaultAutowiredContext(services);
            _currentContext = context;
            var instance = fn();
            if (instance is not null)
            {
                _currentContext.Add(instance);
            }
            context.Wired();
            return instance;
        }
        finally
        {
            _currentContext = null;
        }
    }


    /// <summary>
    /// 获取带Scope服务的服务
    /// </summary>
    /// <remarks>为了解决原始注入方式不支持在单例服务中注入Scope服务的问题</remarks>
    public static T? GetServiceWithScope<T>(this IServiceProvider services)
    {
        return services.Autowired(() => services.GetService<T>());
    }


    private static void Wired(this IAutowiredContext context)
    {
        var invoker = context.GetService<IAutowiredInvoker>() ??
            new DefaultAutowiredInvoker(context.GetService<ILogger<DefaultAutowiredInvoker>>());
        for (int i = 0; i < context.Count; i++)
        {
            var instance = context[i];
            if (instance is null)
            {
                return;
            }
            var descriptors = AutowiredDescriptor.FormType(instance.GetType());
            foreach (var descriptor in descriptors)
            {
                // 调用装配方法
                if (invoker.TryWired(context, descriptor, instance))
                {
                    continue;
                }
            }

        }
    }


    /// <summary>
    /// 添加服务自动装配支持
    /// </summary>
    public static IServiceCollection AddAutowired(this IServiceCollection services, Action<AutowiredConfig>? configure = null)
    {
        var config = new AutowiredConfig();
        configure?.Invoke(config);
        services.Replace(ServiceDescriptor.Singleton(config));


        services.TryAddTransient<IAutowiredContext, DefaultAutowiredContext>();
        services.TryAddSingleton<IAutowiredInvoker, DefaultAutowiredInvoker>();

        services.TryAddSingleton<SingletonServiceProvider>(p => new(p));
        services.TryAddScoped<ScopedServiceProvider>(p => new(p));
        services.TryAddTransient<TransientServiceProvider>(p => new(p));
        services.TryAddScoped(typeof(CrossServiceAccessor<>));

        var builder = FeatureManager.Get<IAutowiredFeature>();
        for (int i = 0; i < services.Count; i++)
        {
            var service = services[i];

            if (service.ServiceType.Assembly == typeof(IAutowiredContext).Assembly)
            {
                continue;
            }

            services[i] = builder.Proxy(services[i]);
        }
        return services;
    }


    /// <summary>
    /// 获取带Scope服务的服务
    /// </summary>
    /// <remarks>为了解决原始注入方式不支持在单例服务中注入Scope服务的问题</remarks>
    public static T? GetServiceWithScope<T>(this IServiceScope scope)
        => scope.ServiceProvider.GetServiceWithScope<T>();
}
